#include "serial.h"
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

serial_handle_t Serial_Init(void *arg, serial_recv_cb_t recv_cb, serial_send_cb_t send_cb)
{
    if (recv_cb == NULL || send_cb == NULL)
        return NULL;

    serial_handle_t handle = malloc(sizeof(serial_t));

    if (handle == NULL)
        return NULL;

    memset(handle, 0, sizeof(serial_t));
    handle->buffer = malloc(SERIAL_BUFFER_SIZE);
    handle->payload = malloc(SERIAL_PAYLOAD_SIZE);

    if (handle->buffer == NULL || handle->payload == NULL)
        return NULL;

    memset(handle->payload, 0, SERIAL_PAYLOAD_SIZE);
    memset(handle->buffer, 0, SERIAL_BUFFER_SIZE);

    handle->arg = arg;
    handle->RecvCallback = recv_cb;
    handle->SendCallback = send_cb;

#if ENABLE_OS_SUPPORT
    handle->rx_complete = xSemaphoreCreateBinary();
    handle->tx_complete = xSemaphoreCreateBinary();
    xSemaphoreGive(handle->tx_complete);
#else
    handle->tx_complete = 1;
#endif
    return handle;
}

void Serial_Deinit(serial_handle_t handle)
{
    if (handle == NULL)
        return;

    if (handle->buffer != NULL)
        free(handle->buffer);
    if (handle->payload != NULL)
        free(handle->payload);

#if ENABLE_OS_SUPPORT
    vSemaphoreDelete(handle->rx_complete);
    vSemaphoreDelete(handle->tx_complete);
#endif
    free(handle);
    handle = NULL;
}

void Serial_Run(serial_handle_t handle)
{
    if (handle == NULL)
        return;
#if ENABLE_OS_SUPPORT
    xSemaphoreTake(handle->rx_complete, portMAX_DELAY);
#else
    if (!handle->rx_complete)
        return;
    handle->rx_complete = 0;
#endif
    handle->RecvCallback(handle);
}

void Serial_Print(serial_handle_t handle, uint8_t *data, uint16_t size)
{
    if (handle == NULL)
        return;

#if ENABLE_OS_SUPPORT
    xSemaphoreTake(handle->tx_complete, portMAX_DELAY);
#else
    while (!handle->tx_complete)
        continue;
    handle->tx_complete = 0;
#endif
    memcpy(handle->buffer, data, size);
    handle->SendCallback(handle, handle->buffer, size);
}

void Serial_Printf(serial_handle_t handle, const char *format, ...)
{
    if (handle == NULL)
        return;

#if ENABLE_OS_SUPPORT
    xSemaphoreTake(handle->tx_complete, portMAX_DELAY);
#else
    while (!handle->tx_complete)
        continue;
    handle->tx_complete = 0;
#endif
    va_list args;
    va_start(args, format);
    int len = vsnprintf((char *)handle->buffer, SERIAL_BUFFER_SIZE, format, args);
    va_end(args);
    handle->SendCallback(handle, handle->buffer, len);
}

void Serial_SendComplete(serial_handle_t handle)
{
#if ENABLE_OS_SUPPORT
    static BaseType_t xHigherPriorityTaskWoken = pdFALSE;
#endif

    if (handle == NULL)
        return;

#if ENABLE_OS_SUPPORT
    xSemaphoreGiveFromISR(handle->tx_complete, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
#else
    handle->tx_complete = 1;
#endif
}
void Serial_RecvComplete(serial_handle_t handle, uint16_t size)
{
#if ENABLE_OS_SUPPORT
    static BaseType_t xHigherPriorityTaskWoken = pdFALSE;
#endif

    if (handle == NULL)
        return;

    handle->size = size;
#if ENABLE_OS_SUPPORT
    xSemaphoreGiveFromISR(handle->rx_complete, &xHigherPriorityTaskWoken);
    portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
#else
    handle->rx_complete = 1;
#endif
}